package com.anlogic.sdk.tools.ui;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Map;

public class ProcessController {
    private Process process;
    private BufferedReader stdOutput;
    private BufferedWriter stdInput;
    
    private StringBuilder output = new StringBuilder();

    private Thread outputReaderThread;
    private volatile boolean running = false;

    private int timeout = 5000;
    
    public void start(Map<String, String> environment, File workDir, String... command) throws IOException {
        ProcessBuilder processBuilder = new ProcessBuilder(command);
        processBuilder.redirectErrorStream(true);
        processBuilder.directory(workDir);
        
        for (Map.Entry<String, String> entry : environment.entrySet()) {
            processBuilder.environment().put(entry.getKey(), entry.getValue());
        }
    	
        process = processBuilder.start();
		
        stdInput = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
    	stdOutput = new BufferedReader(new InputStreamReader(process.getInputStream()));
    	
    	running = true;
    	startOutputReader();
    }
    
    public boolean isRunning() {
    	return running;
    }

    private void sendCommand(String command) throws IOException {
    	stdInput.write(command + "\n");
    	stdInput.flush(); 
    }

    public String runCommand(String command) throws IOException {
        output.setLength(0);
        sendCommand(command);
        sendCommand("echo COMMAND_END");

        synchronized (output) {
            long startTime = System.currentTimeMillis();
            while (!output.toString().contains("COMMAND_END")) {
                try {
                    output.wait(100);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    throw new IOException("Thread interrupted while waiting for command output", e);
                }
                if (System.currentTimeMillis() - startTime > timeout) {
                    break;
                }
            }
        }

        String ret = getAndClearOutput();
        ret = ret.replaceAll("(?m)^.*COMMAND_END.*$", "").trim();
        return ret;
    }

    public void startOutputReader() throws IOException {
        outputReaderThread = new Thread(() -> {
            try {
                String line;
                while ((line = stdOutput.readLine()) != null) {
                    synchronized (output) {
                        output.append(line).append("\n");
                        output.notifyAll();
                    }
                }
            } catch (IOException e) {
                if (running) {
                    e.printStackTrace();
                }
            }
        });
        outputReaderThread.setDaemon(true); 
        outputReaderThread.start();
    }
    
    public String getAndClearOutput() {
        synchronized (output) {
            String result = output.toString();
            output.setLength(0);
            return result;
        }
    }

    public void stop() throws IOException {
        running = false;
        if (outputReaderThread != null && outputReaderThread.isAlive()) {
            outputReaderThread.interrupt();
        }
        if (process != null) {
            process.destroy();
            process.destroyForcibly();
        }
    }
    
	public void setTimeout(int timeout) {
		this.timeout = timeout;
	}
}
